<?php
/**
 * 代码覆盖率统计
 */
define('TERMINAL', 1);
class coverage
{
    private $db;
    private $table;

    public function __construct() {
        global $db_conf;
        $this->db = new db($db_conf);
        $this->table = 'coverage';
    }

    /**
     * 保存当次统计结果
     * @author menghao
     * @param <type> $data
     * @return <type>
     */
    function save($data)
    {
        if(!is_array($data))
        {
            return false;
        }
        foreach($data as $filename => $info)
        {
            $this->save_one($filename, $info);
        }
        return true;
    }

    /**
     * 保存某一文件的覆盖率
     * @author menghao
     * @param <type> $filename
     * @param <type> $info
     * @return <type>
     */
    function save_one($filename, $info)
    {
        if(!is_array($info))
        {
            return false;
        }
        $old = $this->get_one($filename);
        $total = $this->file_line_num($filename);
        
        if(empty($old))
        {
            $covered = sizeof($info);
            $info = serialize($info);
            $this->db->insert($this->table, array('filename'=>$filename, 'info'=>$info, 'covered'=>$covered, 'total'=>$total));
        }
        else
        {
            foreach($info as $line=>$value)
            {
                if(!isset($old[$line]))
                {
                    $old[$line] = 1;
                }
            }
            $covered = sizeof($old);
            $old = serialize($old);
            $this->db->simple_update($this->table, array('info'=>$old, 'covered'=>$covered, 'total'=>$total), array('filename'=>$filename));
        }
    }

    /**
     * 获取某一文件的代码行数
     * @author menghao
     * @param <type> $filename
     * @return <type>
     */
    private function file_line_num($filename)
    {
        return count(@file($filename));
    }

    /**
     * 获取某一文件的统计信息
     * @author menghao
     * @param <type> $filename
     * @return <type>
     */
    function get_one($filename)
    {
        $this->db->prepare("select info from $this->table where filename=?");
        $this->db->set_string($filename);
        $data = $this->db->execute(true);
        if(empty($data))
        {
            return false;
        }
        return unserialize($data['info']);
    }

    /**
     * 所有被统计文件列表
     * @author menghao
     * @return <type>
     */
    function listinfo()
    {
        return $this->db->query("select filename,total,covered from $this->table order by filename asc");
    }

    function show_list()
    {
        $files = $this->listinfo();
        $arr = array();
        foreach($files as $r)
        {
            $arr = array_merge_simple($arr, $this->path2array($r['filename']));
        }
        return "<ul id='coverage'><li>/".$this->output_tree('/',$arr)."</li></ul>";
    }

    /**
     * 显示某文件的覆盖信息
     * @author menghao
     * @param <type> $filename
     */
    function view($filename)
    {
        $numbers = $this->get_one($filename);
        $f = fopen($filename, 'r');

        $count = 0;
        $uncover = 0;
        while($code = fgets($f))
        {
            $count ++;
            $html = $code;
            $html = htmlspecialchars($html);
            
            $html = strtr($html, array(' '=>'&nbsp;&nbsp;'));
            $html = nl2br($html);
            $style = '';
            if(!isset($numbers[$count]) && !$this->nocount($code)){
                $style = "style='color:red'";
                $uncover++;
            }
            echo "<p><span style='color:gray'>$count</span> <span $style>$html</span> </p>\n";
        }

        $range = round((($count-$uncover) / $count)*100);
        echo "<p>totle:$count ----- uncover:$uncover------range:$range%</p>";
    }

    function get_parent_dir($path)
    {
        $leaf = array();
        while($path != dirname($path))
        {
            $leaf[] = array('id'=>$path,'parentid'=>dirname($path),'name'=>'$path');
        }
        return $leaf;
    }


    /**
     * 排除不在统计范围的代码行
     * @author menghao
     * @param <type> $code
     * @return <type>
     */
    function nocount($code)
    {
        $code = trim($code);
        $result = empty($code) || in_array(substr($code, 0, 1), array('*','//','/','<','?','#','{','}'));
        $result = $result || $this->startwith($code, 'function');
        $result = $result || $this->startwith($code, 'class');
        $result = $result || $this->startwith($code, 'private');
        $result = $result || $this->startwith($code, 'public');
        return $result;
    }

    function startwith($string, $needle)
    {
        return substr($string, 0, strlen($needle)) == $needle;
    }
    function path2array($path)
    {
        $arr = explode('/',$path);
        $root = array_shift($arr);
        $child = array_shift($arr);
        if(empty($arr))
        {
            return array($child=>$child);
        }
        $tree = array($child=>$this->path2array('/'.implode('/',$arr)));
        return $tree;
    }

    function output_tree($dir, $arr)
    {
        $html = "<ul>";
        foreach ($arr as $curr => $child) 
        {
            if(is_array($child))
            {
                $newdir= $dir . $curr . '/';
                $html .= "$curr\n";
                $html .= "<li>".$this->output_tree($newdir, $child)."</li>\n";
            }
            else
            {
                global $self_url;
                // $html .= "$curr\n";
                $html .= "<li><a href='{$self_url}?action=view&filename={$dir}{$curr}'>$curr</a></li>\n";
            }
        }
        $html .= "</ul>\n";
        return $html; 
    }

}
?>
